package cn.yshujia.config;


import cn.yshujia.constant.SystemConst;
import cn.yshujia.filter.CaptchaFilter;
import cn.yshujia.filter.JwtAuthenticationTokenFilter;
import cn.yshujia.filter.PasswordAuthenticationFilter;
import cn.yshujia.handler.JwtAccessDeniedHandler;
import cn.yshujia.handler.JwtAuthenticationHandler;
import cn.yshujia.handler.LogoutSuccessHandle;
import cn.yshujia.service.impl.UserLoginService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;


/**
 * @author YShuJia
 * @description security认证
 * @create 2024/4/23
 */

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
	
	@Resource
	private CaptchaFilter captchaFilter;
	
	@Resource
	private JwtAuthenticationHandler jwtAuthenticationHandler;
	
	@Resource
	private JwtAccessDeniedHandler jwtAccessDeniedHandler;
	
	@Resource
	private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
	
	@Resource
	private LogoutSuccessHandle logoutSuccessHandle;
	
	@Resource
	private RememberMeServices rememberMeServices;
	
	@Resource
	private UserLoginService userLoginService;
	
	@Value ("${jwt.expire}")
	private Long validitySeconds;
	
	
	private int getValiditySeconds() {
		return (int) (validitySeconds / 1000L);
	}
	
	@Bean
	public InitializingBean initializingBean() {
		return () -> SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
	}
	
	@Bean
	RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
		TokenBasedRememberMeServices.RememberMeTokenAlgorithm encodingAlgorithm = TokenBasedRememberMeServices.RememberMeTokenAlgorithm.SHA256;
		TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(SystemConst.REMEMBER_KEY, userDetailsService, encodingAlgorithm);
		rememberMe.setMatchingAlgorithm(TokenBasedRememberMeServices.RememberMeTokenAlgorithm.MD5);
		return rememberMe;
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		// 密码加密 默认10
		return new BCryptPasswordEncoder(16);
	}
	
	@Bean
	public AuthenticationManager authenticationManager(UserLoginService userLoginService, PasswordEncoder passwordEncoder) throws Exception {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userLoginService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);
		return new ProviderManager(authenticationProvider);
	}
	
	@Bean
	public PasswordAuthenticationFilter customAuthFilter(AuthenticationManager authenticationManager) {
		PasswordAuthenticationFilter filter = new PasswordAuthenticationFilter(authenticationManager);
		filter.setAuthenticationManager(authenticationManager);
		return filter;
	}
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
		http.authorizeHttpRequests(auth ->
				auth.anyRequest().permitAll());
		http.formLogin(auth ->
						auth.loginProcessingUrl(SystemConst.LOGIN)
								.permitAll())
				.addFilterAt(customAuthFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
		
		http.rememberMe(re ->
				re.tokenValiditySeconds(getValiditySeconds())
						.rememberMeServices(rememberMeServices));
		http.logout(auth ->
				auth.logoutUrl(SystemConst.LOGIN_OUT)
						.logoutSuccessHandler(logoutSuccessHandle)
						.permitAll());
		
		http.exceptionHandling(auth ->
				auth.authenticationEntryPoint(jwtAuthenticationHandler)
						.accessDeniedHandler(jwtAccessDeniedHandler));
		
		http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
		
		http.csrf(AbstractHttpConfigurer::disable);
		
		http.cors(auth ->
				auth.configure(http));
		
		return http.build();
	}
}